#
# ComicLoader.py
# (c) 2008 Will Baker.
#

import os

from threading import Condition

from Plugin import Plugin
from Page import Page
from Job import Job
from JobQueue import JobQueue

class ComicLoader:
    """
    A class for loading Comics. This manages the comic loader plugins and assigns 
    the appropriate plugin to do the loading depending on the Comic type.
    """
    
    plugins = []
    job_queues = {}
    
    def __init__( self, event_sink ):
        self.event_sink = event_sink
        self.done = False
        self.lock = Condition()
                
    def load_all_pages( self, comic ):
        """
        Loads all pages into a Comic.
        """
        plugin = ComicLoader.find_plugin_by_extension( comic.extension )
        
        if plugin != None:
            queue = ComicLoader.job_queues[plugin]
            LoadAllPages( comic, plugin, queue, self )
            
    def load_single_page( self, comic, page ):
        """
        Loads a single page into a Comic.
        """
        plugin = ComicLoader.find_plugin_by_extension( comic.extension )
        if plugin != None:
            queue = ComicLoader.job_queues[plugin]
            LoadSinglePage( comic, page, plugin, queue, self )
        
    def on_page_count_loaded( self, comic, page_count ):
        """
        Handle the page count being loaded for a Comic. This will occur inside
        a JobThread, not generally in the main thread.
        """
        self.lock.acquire()
        if not self.done:
            comic.page_count = page_count
            self.event_sink.on_page_count_loaded()
        self.lock.release()
        
    def on_page_data_loaded( self, comic, path, page, data ):
        """
        Handle the page data being loaded for a Comic. This will occur inside
        a JobThread, not generally in the main thread.
        """
        self.lock.acquire()
        if not self.done:
            self.event_sink.on_page_loaded( Page(page, data) )
        self.lock.release()
    
    def stop( self ):
        """
        Flags the ComicLoader as stopped. This will inform any Jobs created by this
        ComicLoader that they should stop as soon as they can.
        """
        self.lock.acquire()
        
        self.done = True
        self.event_sink = None
        
        self.lock.release()

    def add_plugin( cls, path ):
        """
        Loads a plugin from the given path.
        """
        plugin = Plugin( path )
        queue = JobQueue( plugin.name, plugin.pool_size )
        
        ComicLoader.plugins.append( plugin )
        ComicLoader.job_queues[plugin] = queue
        
    add_plugin = classmethod( add_plugin )
    
    def load_plugins( cls, path, platform ):
        """
        Process the plugins directory, loading each of the plugins found within
        in the appropriate order.
        
        The order in which the plugins are added is important - the first plugin
        found will be the one returned. The user plugins should be loaded first,
        then the platform specific plugins, then the standard plugins.
        """
        ComicLoader.stop_job_queues()
        
        user = os.path.join( path, "user" )
        platform = os.path.join( path, os.path.join("platform", platform) )
        standard = os.path.join( path, "standard" )

        for path in [user, platform, standard]:
            if os.path.exists( path ):
                for (root, directories, files) in os.walk( path ):
                    for f in files:
                        filename = os.path.join( root, f )
                        (_, ext) = os.path.splitext( filename )
                        if ext == ".py":
                            ComicLoader.add_plugin( filename )
                            
        ComicLoader.start_job_queues()
        
    load_plugins = classmethod( load_plugins )
    
    def find_plugin_by_extension( cls, extension ):
        """
        Attempts to find a plugin by extension.
        """
        for p in ComicLoader.plugins:
            if extension in p.extensions:
                return p
            
        return None
    find_plugin_by_extension = classmethod( find_plugin_by_extension )
    
    def start_job_queues( cls ):
        """
        Start each of the JobQueues.
        """
        for q in ComicLoader.job_queues.values():
            q.start()
    start_job_queues = classmethod( start_job_queues )
            
    def stop_job_queues( cls ):
        """
        Stop each of the JobQueues.
        """
        for q in ComicLoader.job_queues.values():
            q.stop()
    stop_job_queues = classmethod( stop_job_queues )
    
    def clear( cls ):
        """
        Clear the pending Jobs in each of the JobQueues.
        """
        for q in ComicLoader.job_queues.values():
            q.clear()
    clear = classmethod( clear )

class LoadAllPages(Job):
    """
    A Job which loads all the pages in a comic using a plugin.
    """
    def __init__( self, comic, plugin, queue, loader ):
        self.comic = comic
        self.plugin = plugin
        self.loader = loader
        Job.__init__( self, Job.HighPriority, queue )
        
    def start( self ):
        """
        Start loading all pages for the Comic.
        """
        self.plugin.load_all_pages( self.loader, self.comic )
        
class LoadSinglePage(Job):
    """
    A Job which loads a single page in a comic using a plugin.
    """
    def __init__( self, comic, page, plugin, queue, loader ):
        self.comic = comic
        self.page = page
        self.plugin = plugin
        self.loader = loader
        Job.__init__( self, Job.LowPriority, queue )
        
    def start( self ):
        """
        Start loading a single page for the Comic.
        """
        self.plugin.load_single_page( self.loader, self.comic, self.page )
